﻿using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;
using System.Text;

namespace CoreShop.App.Units.Interface
{
    public class AbcpNative
    {
        [DllImport("kernel32.dll", EntryPoint = "LoadLibrary")]
        private static extern IntPtr LoadLibrary(
            [MarshalAs(UnmanagedType.LPStr)] string lpLibFileName);

        [DllImport("kernel32.dll", EntryPoint = "GetProcAddress")]
        private static extern IntPtr GetProcAddress(IntPtr hModule,
            [MarshalAs(UnmanagedType.LPStr)] string lpProcName);

        [DllImport("kernel32.dll", EntryPoint = "FreeLibrary")]
        private static extern bool FreeLibrary(IntPtr hModule);

        // =====================================

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate void type_bp_api_set_isv_lan(int type);
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate void type_abcp_init(int seendId,
            [MarshalAs(UnmanagedType.LPStr)] string appId,
            [MarshalAs(UnmanagedType.LPStr)] string appVersion,
            [MarshalAs(UnmanagedType.LPStr)] string json,
            CallbackAbcpResponse process, CallbackAbcpResponse finish);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate void type_abcp_zimid_init_for_test(int seendId,
            [MarshalAs(UnmanagedType.LPStr)] string zimmetainfo,
            [MarshalAs(UnmanagedType.LPStr)] string smileType,
            [MarshalAs(UnmanagedType.LPStr)] string smileFlag,
            CallbackAbcpString callback);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate void type_abcp_get_meta_info(int seendId,
            [MarshalAs(UnmanagedType.LPStr)] string appId,
            [MarshalAs(UnmanagedType.LPStr)] string json,
            CallbackAbcpResponse process, CallbackAbcpResponse finish);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate void type_abcp_start_service(int seendId,
            [MarshalAs(UnmanagedType.LPStr)] string appId,
            [MarshalAs(UnmanagedType.LPStr)] string serviceCode,
            [MarshalAs(UnmanagedType.LPStr)] string json,
            CallbackAbcpResponse process, CallbackAbcpResponse finish);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate void type_abcp_stop_service(int seendId,
            [MarshalAs(UnmanagedType.LPStr)] string appId,
            [MarshalAs(UnmanagedType.LPStr)] string serviceCode,
            [MarshalAs(UnmanagedType.LPStr)] string json,
            CallbackAbcpResponse process, CallbackAbcpResponse finish);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate void type_abcp_call(int seendId,
            [MarshalAs(UnmanagedType.LPStr)] string appId,
            [MarshalAs(UnmanagedType.LPStr)] string callMethod,
            [MarshalAs(UnmanagedType.LPStr)] string json,
            CallbackAbcpResponse process, CallbackAbcpResponse finish);

        // =====================================

        // 回调字符串会出现乱码, 感觉像是C#编解码的时候会有点BUG
        // 有合作方反馈下面的写法能解决问题 [MarshalAs(UnmanagedType.LPStr),SizeConst=8000]
        // 但是这个属性只针对4.7以上的有用，framework3.5不支持
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void CallbackAbcpResponse(int seedId, int code,
            [MarshalAs(UnmanagedType.LPStr)] string subCode,
            [MarshalAs(UnmanagedType.LPStr)] string subMsg,
            [MarshalAs(UnmanagedType.LPStr)] string result);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void CallbackAbcpString(int seedId,
            [MarshalAs(UnmanagedType.LPStr)] string result);

        private IntPtr mDylibAPI = IntPtr.Zero;
        private string mDylibFile = "";

        private string PathFolderSysDepends()
        {
            string sysDepends = "";
            if (IntPtr.Size == 4)
            {
                sysDepends = "C:/opt/sysOSLibs/x86/";
            }
            else if (IntPtr.Size == 8)
            {
                sysDepends = "C:/opt/sysOSLibs/x64/";
            }
            return sysDepends;
        }
        public void InitAPIDylib(string dylib)
        {   // C# support windows only ...
            mDylibFile = dylib;
            string sysDepends = PathFolderSysDepends();
            string cacheCurrentDir = Directory.GetCurrentDirectory();
            if (Directory.Exists(sysDepends))
            {
                Directory.SetCurrentDirectory(sysDepends);
                IEnumerable<string> files = Directory.EnumerateFiles(sysDepends);
                foreach (var file in files)
                {
                    if (Path.GetExtension(file) != ".dll")
                    {
                        System.Console.WriteLine("### NOT dylib file! [{0}]", file);
                        continue;
                    }
                    IntPtr ptr = LoadLibrary(file);
                    if (ptr == IntPtr.Zero)
                    {
                        System.Console.WriteLine("### LoadLibrary failed! [{0}]", file);
                    }
                }
            }
            if (File.Exists(dylib))
            {
                mDylibAPI = LoadLibrary(dylib); 
            }
            else
            {
                System.Console.WriteLine("### ERROR: NO API lib file to load! [{0}]", dylib);
            }
            
            Directory.SetCurrentDirectory(cacheCurrentDir);
        }

        // ============================

        public void BPCallInit(int seendId,
            string appId, string appVersion, string json,
            CallbackAbcpResponse process, CallbackAbcpResponse finish)
        {
            if (mDylibAPI == IntPtr.Zero)
            {   // 具体错误参考 sdk-bpaas-cpp\source\base\error_code.h
                string subMsg = "[api] load failed! libFile=" + mDylibFile;
                finish(seendId, 3/*RESULT_CODE_INVALID_STATE*/, "E69001", subMsg, "");
                return;
            }

            try
            {
                IntPtr ptrAPI = GetProcAddress(mDylibAPI, "bp_api_set_isv_lan");
                if (ptrAPI != IntPtr.Zero)
                {
                    type_bp_api_set_isv_lan func = (type_bp_api_set_isv_lan)
                        Marshal.GetDelegateForFunctionPointer(ptrAPI, typeof(type_bp_api_set_isv_lan));
                    func(1); // 0:cpp | 1:cshap | 2:jni | 3:jna
                }
            } catch (SystemException) { }

            IntPtr api = GetProcAddress(mDylibAPI, "abcp_init");
            if (api == IntPtr.Zero)
            {   // 具体错误参考 sdk-bpaas-cpp\source\base\error_code.h
                string subMsg = "[api] abcp_init NOT exist! libFile=" + mDylibFile;
                finish(seendId, 3/*RESULT_CODE_INVALID_STATE*/, "E69001", subMsg, "");
                return;
            }

            try
            {
                type_abcp_init func = (type_abcp_init)Marshal.GetDelegateForFunctionPointer(api, typeof(type_abcp_init));
                func(seendId, appId, appVersion, json, process, finish);
            }
            catch (SystemException e)
            {
                string subMsg = "[api] abcp_init run exception!" + e.ToString();
                Console.Out.WriteLine("[{0}:{1}] ", "func", subMsg);
                finish(seendId, 3/*RESULT_CODE_INVALID_STATE*/, "E69001", subMsg, "");
            }
        }

        public void BPGetMetaInfo(int seendId, 
            string appId, string json,
            CallbackAbcpResponse process, CallbackAbcpResponse finish)
        {
            if (mDylibAPI == IntPtr.Zero)
            {   // 具体错误参考 sdk-bpaas-cpp\source\base\error_code.h
                string subMsg = "[api] load failed! libFile=" + mDylibFile;
                finish(seendId, 3/*RESULT_CODE_INVALID_STATE*/, "E69001", subMsg, "");
                return;
            }
            IntPtr api = GetProcAddress(mDylibAPI, "abcp_get_meta_info");
            if (api == IntPtr.Zero)
            {   // 具体错误参考 sdk-bpaas-cpp\source\base\error_code.h
                string subMsg = "[api] abcp_get_meta_info NOT exist! libFile=" + mDylibFile;
                finish(seendId, 3/*RESULT_CODE_INVALID_STATE*/, "E69001", subMsg, "");
                return;
            }

            try
            {
                type_abcp_get_meta_info func = (type_abcp_get_meta_info)Marshal.GetDelegateForFunctionPointer(api, typeof(type_abcp_get_meta_info));
                func(seendId, appId, json, process, finish);
            }
            catch (SystemException e)
            {
                string subMsg = "[api] abcp_get_meta_info run exception!" + e.ToString();
                Console.Out.WriteLine("[{0}:{1}] ", "func", subMsg);
                finish(seendId, 3/*RESULT_CODE_INVALID_STATE*/, "E69001", subMsg, "");
            }
        }

        public void BPStartService(int seendId, 
            string appId, string serviceCode, string json,
            CallbackAbcpResponse process, CallbackAbcpResponse finish)
        {
            if (mDylibAPI == IntPtr.Zero)
            {   // 具体错误参考 sdk-bpaas-cpp\source\base\error_code.h
                string subMsg = "[api] load failed! libFile=" + mDylibFile;
                finish(seendId, 3/*RESULT_CODE_INVALID_STATE*/, "E69001", subMsg, "");
                return;
            }
            IntPtr api = GetProcAddress(mDylibAPI, "abcp_start_service");
            if (api == IntPtr.Zero)
            {   // 具体错误参考 sdk-bpaas-cpp\source\base\error_code.h
                string subMsg = "[api] abcp_start_service NOT exist! libFile=" + mDylibFile;
                finish(seendId, 3/*RESULT_CODE_INVALID_STATE*/, "E69001", subMsg, "");
                return;
            }

            try
            {
                type_abcp_start_service func = (type_abcp_start_service)Marshal.GetDelegateForFunctionPointer(api, typeof(type_abcp_start_service));
                func(seendId, appId, serviceCode, json, process, finish);
            }
            catch (SystemException e)
            {
                string subMsg = "[api] abcp_start_service run exception!" + e.ToString();
                Console.Out.WriteLine("[{0}:{1}] ", "func", subMsg);
                finish(seendId, 3/*RESULT_CODE_INVALID_STATE*/, "E69001", subMsg, "");
            }
        }

        public void BPStopService(int seendId, 
            string appId, string serviceCode, string json,
            CallbackAbcpResponse process, CallbackAbcpResponse finish)
        {
            if (mDylibAPI == IntPtr.Zero)
            {   // 具体错误参考 sdk-bpaas-cpp\source\base\error_code.h
                string subMsg = "[api] load failed! libFile=" + mDylibFile;
                finish(seendId, 3/*RESULT_CODE_INVALID_STATE*/, "E69001", subMsg, "");
                return;
            }
            IntPtr api = GetProcAddress(mDylibAPI, "abcp_stop_service");
            if (api == IntPtr.Zero)
            {   // 具体错误参考 sdk-bpaas-cpp\source\base\error_code.h
                string subMsg = "[api] abcp_stop_service NOT exist! libFile=" + mDylibFile;
                finish(seendId, 3/*RESULT_CODE_INVALID_STATE*/, "E69001", subMsg, "");
                return;
            }

            try
            {
                type_abcp_stop_service func = (type_abcp_stop_service)Marshal.GetDelegateForFunctionPointer(api, typeof(type_abcp_stop_service));
                func(seendId, appId, serviceCode, json, process, finish);
            }
            catch (SystemException e)
            {
                string subMsg = "[api] abcp_stop_service run exception!" + e.ToString();
                Console.Out.WriteLine("[{0}:{1}] ", "func", subMsg);
                finish(seendId, 3/*RESULT_CODE_INVALID_STATE*/, "E69001", subMsg, "");
            }
        }

        public void ZimIdInitTest(int seendId, 
            string zimmetainfo, string smileType, string smileFlag,
            CallbackAbcpString callback)
        {
            if (mDylibAPI == IntPtr.Zero)
            {
                callback(seendId, "{}");
                return;
            }
            IntPtr api = GetProcAddress(mDylibAPI, "abcp_zimid_init_for_test");
            if (api == IntPtr.Zero)
            {
                callback(seendId, "{}");
                return;
            }

            try
            {
                type_abcp_zimid_init_for_test func = (type_abcp_zimid_init_for_test)Marshal.GetDelegateForFunctionPointer(api, typeof(type_abcp_zimid_init_for_test));
                func(seendId, zimmetainfo, smileType, smileFlag, callback);
            }
            catch (SystemException e)
            {
                string subMsg = "[api] abcp_zimid_init_for_test run exception!" + e.ToString();
                Console.Out.WriteLine("[{0}:{1}] ", "func", subMsg);
                callback(seendId, "{}");
            }
        }

        public void BPSubscribeEvent(int seendId, 
            string appId, string json,
            CallbackAbcpResponse process, CallbackAbcpResponse finish)
        {
            if (mDylibAPI == IntPtr.Zero)
            {   // 具体错误参考 sdk-bpaas-cpp\source\base\error_code.h
                string subMsg = "[api] load failed! libFile=" + mDylibFile;
                finish(seendId, 3/*RESULT_CODE_INVALID_STATE*/, "E69001", subMsg, "");
                return;
            }
            IntPtr api = GetProcAddress(mDylibAPI, "abcp_call");
            if (api == IntPtr.Zero)
            {   // 具体错误参考 sdk-bpaas-cpp\source\base\error_code.h
                string subMsg = "[api] abcp_call NOT exist! libFile=" + mDylibFile;
                finish(seendId, 3/*RESULT_CODE_INVALID_STATE*/, "E69001", subMsg, "");
                return;
            }

            try
            {
                type_abcp_call func = (type_abcp_call)Marshal.GetDelegateForFunctionPointer(api, typeof(type_abcp_call));
                func(seendId, appId, "SubscribeEvent", json, process, finish);
            }
            catch (SystemException e)
            {
                string subMsg = "[api] abcp_call(BPSubscribeEvent) run exception!" + e.ToString();
                Console.Out.WriteLine("[{0}:{1}] ", "func", subMsg);
                finish(seendId, 3/*RESULT_CODE_INVALID_STATE*/, "E69001", subMsg, "");
            }
        }

        public void BPUnsubscribeEvent(int seendId, 
            string appId, string json,
            CallbackAbcpResponse process, CallbackAbcpResponse finish)
        {
            if (mDylibAPI == IntPtr.Zero)
            {   // 具体错误参考 sdk-bpaas-cpp\source\base\error_code.h
                string subMsg = "[api] load failed! libFile=" + mDylibFile;
                finish(seendId, 3/*RESULT_CODE_INVALID_STATE*/, "E69001", subMsg, "");
                return;
            }
            IntPtr api = GetProcAddress(mDylibAPI, "abcp_call");
            if (api == IntPtr.Zero)
            {   // 具体错误参考 sdk-bpaas-cpp\source\base\error_code.h
                string subMsg = "[api] abcp_call NOT exist! libFile=" + mDylibFile;
                finish(seendId, 3/*RESULT_CODE_INVALID_STATE*/, "E69001", subMsg, "");
                return;
            }

            try
            {
                type_abcp_call func = (type_abcp_call)Marshal.GetDelegateForFunctionPointer(api, typeof(type_abcp_call));
                func(seendId, appId, "UnsubscribeEvent", json, process, finish);
            }
            catch (SystemException e)
            {
                string subMsg = "[api] abcp_call(BPUnsubscribeEvent) run exception!" + e.ToString();
                Console.Out.WriteLine("[{0}:{1}] ", "func", subMsg);
                finish(seendId, 3/*RESULT_CODE_INVALID_STATE*/, "E69001", subMsg, "");
            }
        }
    }
}
